home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / c / sbprog10.zip / SBDEVICE.CPP < prev    next >
C/C++ Source or Header  |  1993-09-15  |  11KB  |  352 lines

  1. // SbDevice.cpp
  2.  
  3. // Function definitions for the SbDevice class. Allows a program to make
  4. // use of the Soundblaster's DMA recording/playback modes.
  5.  
  6. // Written by Christopher M. Box (1993).
  7. // Some functions were based on Soundblaster Freedom Project code.
  8.  
  9. #include <conio.h>
  10. #include <process.h>
  11. #include <dos.h>
  12.  
  13. #include "sndclass.h"
  14.  
  15. // Define maximum sampling speeds for SB low-speed mode
  16. #define MAX_LO_PLAY 22222
  17. #define MAX_LO_REC 12048
  18. // Define maximum wait when writing a command to the SB
  19. #define CMD_TIMEOUT 500000UL
  20.  
  21. // Command array used to store SB commands
  22. static byte sb_cmd_data[5];
  23. static volatile byte sb_cmd_len;
  24.  
  25. // Constructor - calls ASM SB detection routine and initialises variables
  26.  
  27. SbDevice::SbDevice(void) {
  28.     Dprint(("Soundblaster initialisation.\r\n"));
  29.     if (dsp_reset()) {
  30.         cprintf("Soundblaster not found.\r\n");
  31.         exists = 0;
  32.     } else {
  33.         exists = 1;
  34.         init_irq();            // Install interrupt handler
  35.         sb_size = 0;
  36.     }
  37. }
  38.  
  39. // Destructor - switches off SB
  40.  
  41. SbDevice::~SbDevice(void) {
  42.     if (exists) {
  43.         Dprint(("Destruct sb device.\r\n"));
  44.         deinit_irq();
  45.         voice(0);          // Turn off voice output
  46.         dsp_reset();       // Reset SB
  47.     }
  48.     prevent_dma(SbDMAchan);
  49. }
  50.  
  51. // Function: set_rate
  52. // Set the sampling rate, subject to the SB's granular speed-setting ability.
  53. // Stores the resulting rate in the 'rate' variable (this is usually near
  54. // to, but not the same as 'new_rate'). Automatically enables high speed
  55. // mode if necessary, but it needs to know the intended data direction
  56. // ('dir') to do this.
  57.  
  58. void SbDevice::set_rate(unsigned new_rate, byte dir) {
  59.     byte tc;              // Time constant
  60.  
  61.     if (!exists) return;
  62.     tc = (byte) (256 - ((1000000L + new_rate/2)/new_rate));
  63.     rate = (unsigned) (1000000L / (256 - tc));
  64.     hi_speed = (rate > (dir == PLAY ? MAX_LO_PLAY : MAX_LO_REC));
  65.     Dprint(("Time constant %i. Hispeed %i.\r\n",(int)tc,hi_speed));
  66.     dsp_cmd(TIME_CONSTANT);         // Command byte for sample rate
  67.     dsp_cmd(tc);                    // Sample rate time constant
  68. }
  69.  
  70. // Function: dsp_cmd
  71. // Send a command byte ('cmd') to the SB, after waiting for the busy flag to
  72. // clear. If the SB locks up and never clears the busy flag, it prints
  73. // an error message and exits.
  74.  
  75. void SbDevice::dsp_cmd(byte cmd) {
  76.     unsigned long wait = 0;
  77.  
  78.     while (inportb(DSP_WRITE_STATUS) & 0x80) {
  79.         if (++wait > CMD_TIMEOUT) {
  80.             cprintf("Timeout while waiting to write command to SB.\r\n");
  81.             exit(1);
  82.         }
  83.     }
  84.     outportb(DSP_WRITE_DATA,cmd);
  85.     Dprint(("Waited %lu to write %x.\r\n",wait,(int)cmd));
  86. }
  87.  
  88. // Function: voice
  89. // Enables or disables the SB's voice output according to 'state'.
  90.  
  91. void SbDevice::voice(int state) {
  92.     dsp_cmd((state) ? SPEAKER_ON : SPEAKER_OFF);
  93. }
  94.  
  95. // Function: buf_dma_start
  96. // Starts buffered DMA to/from the SB. 'buffer' points to the start of
  97. // the buffer area, and 'buflen' holds the length of the buffer (both halves)
  98. // in bytes. 'dir' sets the direction.
  99.  
  100. void SbDevice::buf_dma_start(byte far *buffer, unsigned buflen, byte dir) {
  101.     byte im, tm;             // Interrupt masks
  102.  
  103.     if (!exists) {
  104.         cprintf("Fatal error: no SB exists.\r\n");
  105.         exit(-1);
  106.     }
  107.  
  108.     im = inportb(0x21);
  109.     tm = ~(1 << SbIRQ);
  110.     outportb(0x21,im & tm);      // Enable SB interrupt
  111.     enable();
  112.  
  113.     // First ensure that the channel is inactive before setting it up
  114.     if (prevent_dma(SbDMAchan)) {
  115.         cprintf("DMA: %s\r\n", dma_errlist[dma_errno]);
  116.         exit(1);
  117.     }
  118.  
  119.     // Next prepare the DMA controller
  120.     if (dma_setup(SbDMAchan,buffer,buflen-1,dir)) {
  121.         cprintf("DMA setup: %s\r\n", dma_errlist[dma_errno]);
  122.         exit(1);
  123.     }
  124.     direction = dir;
  125.  
  126.     // Setup Soundblaster for transfer
  127.     set_rate(rate, direction);
  128.     voice(dir == PLAY);
  129.     sb_size = 0;            // SB card forgets last buffer size so remind it
  130.     set_sb_cmds(buflen);    // Work out the commands to send
  131.     int i=0;
  132.     while (i < sb_cmd_len) dsp_cmd(sb_cmd_data[i++]);     // And send them
  133.  
  134.     Dprint(("Buffered DMA started - length %u\r\n",buflen));
  135. }
  136.  
  137. // Function: set_sb_cmds
  138. // Private function to work out the necessary commands to start an SB
  139. // transfer, and store these in an array. 'buflen' tells it the total
  140. // number of bytes to play/record.
  141.  
  142. #define STORE(x) sb_cmd_data[sb_cmd_len++] = (x)
  143.  
  144. void SbDevice::set_sb_cmds(unsigned buflen) {
  145.     sb_cmd_len = 0;
  146.     unsigned bl = buflen - 1;
  147.     if (hi_speed) {              // Different commands in hispeed mode
  148.         if (buflen != sb_size) {  // Only need to set the length if it hasn't
  149.             STORE(SET_HS_SIZE);   // been used before
  150.             STORE(bl & 0xff);
  151.             STORE(bl >> 8);
  152.         }
  153.         STORE((direction == PLAY) ? HS_DAC : HS_ADC);
  154.     } else {
  155.         STORE((direction == PLAY) ? DMA_8_BIT_DAC : DMA_ADC);
  156.         STORE(bl & 0xff);       // Always set the length in low-speed mode
  157.         STORE(bl >> 8);
  158.     }
  159.     sb_size = buflen;
  160.     Dprint(("%i commands stored.\r\n", (int)sb_cmd_len));
  161. }
  162.  
  163. // Function: process_keys
  164. // Called by buf_dma_lo and buf_dma_hi whenever a key has been pressed
  165. // (kbhit() is true) while waiting for the DMA to reach the end
  166. // of a buffer. It defines the actions to be taken, depending on the key.
  167. // Returns 0 if the calling function should continue, otherwise it returns
  168. // the character that was pressed.
  169.  
  170. int SbDevice::process_keys(void) {
  171.     int c;
  172.     c = getch();
  173.     if (c == 'p') {          // Key 'p' means pause
  174.         halt();
  175.         getch();
  176.         cont();
  177.     } else {                 // Anything else stops the SB DMA
  178.         if (dsp_reset()) cprintf("Bad dsp reset.");
  179.         Dprint(("Terminated.\r\n"));
  180.         return(c);
  181.     }
  182.     return 0;
  183. }
  184.  
  185. // Function: buf_dma_lo
  186. // Called when the foreground routine has finished its task and wishes
  187. // to wait for the DMA to complete the low half-buffer. It checks for overrun
  188. // (DMA already into the high buffer) and keyboard activity. It returns zero
  189. // when the DMA has completed, and non-zero if a key has been pressed. The
  190. // single argument, 'len', defines the length of the low buffer.
  191.  
  192. int SbDevice::buf_dma_lo(unsigned len) {
  193.     if (len > sb_size) {
  194.         cprintf("Bad length.");
  195.         exit(1);
  196.     }
  197.     register unsigned ad = dma_addr();
  198.     Dprint(("Lo addr = %X. ",ad));
  199.     // Current address needs to be between 0 and len, otherwise
  200.     // something has gone wrong.
  201.     if (ad > len) {
  202.         Dprint(("Overrun - skipping buffer."));
  203.         while ((ad = dma_addr()) > len);            // Skip high buffer
  204.     }
  205.     Dprint(("\r\n"));
  206.     // If ad==0 then either the CPU is very fast, or the transfer has already
  207.     // finished and auto-initialised. We assume the second case. The first
  208.     // only occurs at the start, and is dealt with in file_dma().
  209.     int c;
  210.     while ((ad=dma_addr()) < len && ad) {
  211.         // Terminate waiting loop if either:
  212.         // 1. DMA current address reaches 'len' or more
  213.         // 2. Current address is zero (after an auto-initialise)
  214.         // Meanwhile, check for keypresses
  215.         if (kbhit()) {
  216.             if ((c=process_keys()) != 0) return(c);
  217.         }
  218.     }
  219.     // Now set up variables for high buffer.
  220.     lo_buf_sz = len;
  221.     if (sb_size == len) {
  222.         Dprint(("Finished buffered DMA.\r\n"));
  223.     }
  224.     return 0;
  225. }
  226.  
  227. // Function: buf_dma_hi
  228. // This is the corresponding high-buffer function. An extra argument is
  229. // required ('next_buflen') which defines the size of the buffer length to
  230. // be used after the high buffer has completed. This is usually the same
  231. // as the current length, or perhaps zero.
  232.  
  233. int SbDevice::buf_dma_hi(unsigned len, unsigned next_buflen) {
  234.     len += lo_buf_sz;
  235.     if (len != sb_size) {
  236.         cprintf("Bad length.");
  237.         exit(1);
  238.     }
  239.     register unsigned ad = dma_addr();
  240.     Dprint(("Hi addr = %X. ",ad));
  241.     // Address needs to be between buf_size and len
  242.     if (ad < lo_buf_sz) {
  243.         Dprint(("Overrun - skipping buffer. "));
  244.         while ((ad = dma_addr()) < lo_buf_sz);    // Skip the low buffer
  245.     }
  246.     Dprint(("\r\n"));
  247.     int c;
  248.     if (next_buflen) {
  249.         // There's another DMA after this so set up an interrupt routine.
  250.         // dma count does not change (assumes buffer len will not get longer)
  251.         set_sb_cmds(next_buflen);
  252.         setvect(SbIRQ+8, sb_buf_dma_int);    // Select the interrupt handler
  253.         while(sb_cmd_len) {        // Wait for end-of-dma interrupt
  254.             if (kbhit()) {      // (which will set sb_cmd_len back to zero).
  255.                 if ((c=process_keys()) != 0) {
  256.                     setvect(SbIRQ+8, sb_dummy_int);
  257.                     return(c);
  258.                 }
  259.             }
  260.         }
  261.         Dprint(("Interrupt received.\r\n"));
  262.         setvect(SbIRQ+8, sb_dummy_int);     // Select the inactive (dummy) handler
  263.     } else {
  264.         // Last block, so don't need to catch the interrupt
  265.         Dprint(("Final high block.\r\n"));
  266.         unsigned ad;
  267.         while ((ad=dma_addr()) < len && ad) {
  268.             if (kbhit() && ((c=process_keys()) != 0)) return(c);
  269.         }
  270.         Dprint(("Finished buffered DMA.\r\n"));
  271.     }
  272.     return 0;
  273. }
  274.  
  275. // Function: sb_buf_dma_int
  276. // Handler for the "dma-complete" interrupt from the SB. As class member
  277. // functions cannot be interrupt handlers, it is a global function.
  278. // This is the active handler - it sends out the stored list of SB
  279. // commands upon receipt of the interrupt, thus starting a new transfer
  280. // immediately. Note that sb_cmd_len is declared volatile as it is
  281. // decremented by this function, and is used as a signal to the foreground
  282. // routine that the interrupt has occurred.
  283.  
  284. void far interrupt sb_buf_dma_int(...) {
  285.     inportb(DSP_DATA_AVAIL);      // Acknowledge the interrupt
  286.     outportb(0x20,0x20);          // Send EOI command to int controller
  287.     enable();                     // and allow further interrupts
  288.  
  289.     byte *data = sb_cmd_data;
  290.     sb_cmd_len++;
  291.     while ((--sb_cmd_len) > 0) {
  292.         while (inportb(DSP_WRITE_STATUS) & 0x80); // Wait for busy flag to clear
  293.         outportb(DSP_WRITE_DATA,*(data++));       // and send out new data
  294.     }
  295.     // sb_cmd_len finishes with 0
  296. }
  297.  
  298. // Function: sb_dummy_int
  299. // Dummy interrupt handler. Does nothing but acknowledge the interrupt.
  300.  
  301. void far interrupt sb_dummy_int(...) {
  302.     inportb(DSP_DATA_AVAIL);
  303.     outportb(0x20,0x20);
  304.     enable();
  305. }
  306.  
  307. // Function: init_irq
  308. // Initialises the interrupts.
  309.  
  310. void SbDevice::init_irq(void) {
  311.     disable();
  312.     OldIRQ = getvect(0x08 + SbIRQ);       // Save the old vector
  313.  
  314.     setvect(0x08 + SbIRQ,sb_dummy_int);   // Install the dummy handler
  315.     enable();
  316. }
  317.  
  318. // Function: deinit_irq
  319. // Removes our interrupt handler and restores the original one.
  320.  
  321. void SbDevice::deinit_irq(void) {
  322.     byte tm;         // mask
  323.  
  324.     disable();
  325.     tm = inportb(0x21);
  326.     outportb(0x21, tm | (1 << SbIRQ));
  327.     setvect(0x08 + SbIRQ,OldIRQ);
  328.     enable();
  329. }
  330.  
  331. // Function: halt
  332. // Temporarily stops sound playback/recording.
  333.  
  334. void SbDevice::halt(void) {
  335.     if (hi_speed) {               // In high speed mode the only way
  336.         prevent_dma(SbDMAchan);   // is to disable DMA transfers
  337.     } else {
  338.         dsp_cmd(HALT_DMA);        // At low speed we use the "official" way
  339.     }
  340. }
  341.  
  342. // Function: cont
  343. // Continues sound after a halt().
  344.  
  345. void SbDevice::cont(void) {
  346.     if (hi_speed) {
  347.         allow_dma(SbDMAchan);
  348.     } else {
  349.         dsp_cmd(CONTINUE_DMA);
  350.     }
  351. }
  352.